<?php

namespace DeCMF\Core\UI\ListView;

/**
 * 列表视图中,每“行”内的“单元格”字段相关处理
 */
class Grid
{
    /**
     *
     * @var array “单元格Grid”定义
     */
    protected $def = [
        'title' => '', 
        'field' => [], 
        'format' => '', 
        'href' => ''
    ];
    
    /**
     *
     * @var array 一个“单元格Grid”可能依赖所在“行ListItem”所有值,所以要传递过来
     */
    protected $ref = [];
    
    /**
     *
     * @var array 内置链接占位符
     */
    protected $builtinLinks = [];
    
    /**
     *
     * @var $this 
     */
    protected static $instance;

    /**
     * 
     * @param array $def “单元格”定义,格式如:
     * <pre>
     * [
     *      'title' => '标题',
     *      'field' => [//一个单元格的内容可能由多个字段拼接而成
     *          'title', //如果是文档模型要显示列表查看连接,必须放第一个
     *          'uid|get_nickname',//可通过右侧回调函数对字段值进行修饰
     *          'age'
     *      ],
     *      //最终要输出的文本内容格式 []内的字符只能a-z_
     *      'format' => '[title]操作记录: [uid] 的年龄为 [age] 岁',
     *      //链接格式,如果包含|,则format字段失效
     *      'href' => 'index?pid=[id]&model=[model_id]&cate_id=[category_id]'
     * ]
     * </pre>
     * @param array $ref 一个“单元格”可能要访问所在“行”其它值,数据格式如:
     * <pre>
     * [
     *      'id' => 1,
     *      'type' => '目录',
     * ]
     * </pre>
     */
    public function __construct(array $def, array $ref)
    {
        $this->setDef($def);
        $this->setRef($ref);
    }
    
    /**
     * 静态方法,用于快速调用Grid::parse()
     * @param array $ref
     * @param array $def
     * @return string
     */
    public static function one(array $ref, array $def)
    {
        return (new static($def, $ref))->parse();
    }
    
    /**
     * 
     * 根据定义规则解析列表项中要呈现的字段
     * @return string
     */
    public function parse()
    {
        // 获取当前字段数据
        $item = [];
        foreach ($this->def['field'] as $field) {
            $array = explode('|', $field);
            $temp = $this->ref[$array[0]];
            // 函数支持
            if (isset($array[1])) {
                $temp = call_user_func($array[1], $temp);
            }
            $item[$array[0]] = $temp;
        }
        
        if (empty($this->def['href'])) {
            return $this->format($item, $this->def['format']);
        }
        
        $links = explode(',', $this->def['href']);
        foreach ($links as $link) {
            $val[] = $this->parseLink($link, $item, $this->builtinLinks);
        }

        return implode(' ', $val);
    }
    
    /**
     * 构造带链接的Grid项内容
     * link参数的传值形式大致为:
     * - '[username]': 即直接返回username字段的值,不带链接;
     * - '[LIST] [DELETE] ...': 包含内置链接占位符的会被替换为固定格式;
     * - 'index?age=[age] ...': 包含其他字段值也会解析;
     * - 'view?id=[id]|查看文章': |右侧直接作为href的文本,否则解析$format值作为href的文本;
     * @param string $link  
     * @param array $item
     * @return string
     */
    protected function parseLink($link, array &$item)
    {
        $array = explode('|', $link);
        $href = $array[0];
        if (preg_match('/^\[([a-z_]+)\]$/', $href, $matches)) {
            return $item[$matches[1]];
        }
                
        // 替换系统特殊字符串
        if ($this->builtinLinks) {
            $href = str_replace(
                    array_keys($this->builtinLinks),
                    array_values($this->builtinLinks), 
                    $href
            );
        }
        
        // 替换数据变量
        $href = preg_replace_callback(
                '/\[([a-z_]+)\]/', 
                function ($match) {
                    return isset($this->ref[$match[1]]) ? $this->ref[$match[1]] : '';
                }, 
                $href
        );
        
        return '<a href="' . U($href) . '">' 
                . (isset($array[1]) 
                    ? $array[1] 
                    : $this->format($item, $this->def['format'])) 
                . '</a>';
    }
    
    /**
     * 按指定格式整理成要输出的数据
     * @param array $item
     * @param string $format 值不为空时代表以$format中格式输出
     * @return string
     */
    private function format(array $item, $format = '')
    {
        if (empty($format)) {
            return implode(' ', $item);
        }
        
        return preg_replace_callback(
                '/\[([a-z_]+)\]/', 
                function ($match) use ($item) {
                    return isset($item[$match[1]]) ? $item[$match[1]] : '';
                },
                $format
        );
    }
    
    /**
     * 
     * @param array $def
     */
    protected function setDef(array $def)
    {
        if (!empty($def)) {
            $this->def = array_merge($this->def, $def);
        }
    }
    
    /**
     * 
     * @param array $ref
     */
    protected function setRef(array $ref)
    {
        if (!empty($ref)) {
            $this->ref = array_merge($this->ref, $ref);
        }
    }

}
